package org.sfx.collections

import javafx.beans.InvalidationListener
import javafx.collections.ListChangeListener
import javafx.collections.ObservableList
import javafx.collections.FXCollections
import sun.reflect.generics.reflectiveObjects.NotImplementedException
import org.sfx.rich.Enrich._
import scala.collection.JavaConverters._
import scala.collection.mutable.Buffer
/*
class CompositeObservableList[E] extends ObservableList[E] {
  val lists: ObservableList[ObservableList[E]] = FXCollections.observableArrayList()
  val listeners: Buffer[ListChangeListener[E]] = Buffer[ListChangeListener[E]]()
  val shadowLists: ObservableList[ObservableList[E]] = FXCollections.observableArrayList()
  val shadowListeners: Buffer[ListChangeListener[E]] = Buffer[ListChangeListener[E]]()
  private val backingList: ObservableList[E] = FXCollections.observableArrayList()
  
  lists.addChangeListener { listsChange =>
    listsChange.foreach {
      // Remove listeners and lists
      val slicedListeners = listeners.slice(listsChange.from, listsChange.removedTo)
      val zippedSlice = slicedListeners.zip(listsChange.removed.asScala)
      zippedSlice.foreach { case (listener, list) =>
        list.removeListener(listener)
      }
      shadowLists.remove(listsChange.from, listsChange.removedTo)
      listeners.remove(listsChange.from, listsChange.removedSize)
      
      // Add listeners and lists
      val newZippedShadowLists = listsChange.addedSubList.asScala.map { list =>
        val newShadowList = FXCollections.observableArrayList(list)
        var newListener: ListChangeListener[E] = null
        newListener = list.addChangeListener { listChange =>
          listChange.foreach {
            newShadowList.remove(listChange.from, listChange.removedTo)
            newShadowList.addAll(listChange.from, listChange.addedSubList)
          }
        }
        (newListener, newShadowList)
      }
      listeners.insertAll(listsChange.from, newZippedShadowLists.map(_._1))
      shadowLists.addAll(listsChange.from, newZippedShadowLists.map(_._2).asJava)
    }
  }
  
  shadowLists.addChangeListener { shadowListsChange =>
    shadowListsChange.foreach {
      val sumSize = shadowLists.asScala.take(shadowListsChange.from).foldLeft(0)(_ + _.size)

      // Remove listeners and items
      val slicedShadowListeners = shadowListeners.slice(shadowListsChange.from, shadowListsChange.removedTo)
      val removedZippedShadowLists = slicedShadowListeners.zip(shadowListsChange.removed.asScala)
      val sumRemovedSize = removedZippedShadowLists.map { case (shadowListener, shadowList) =>
        shadowList.removeListener(shadowListener)
        shadowList
      }.foldLeft(0)(_ + _.size)
      shadowListeners.remove(shadowListsChange.from, shadowListsChange.removedSize)
      backingList.remove(sumSize, sumSize + sumRemovedSize)
      
      // Add listeners and items
      val newZipped = shadowListsChange.addedSubList.asScala.map { shadowList =>
        var newShadowListener: ListChangeListener[E] = null
        newShadowListener = shadowList.addChangeListener { shadowListChange =>
          shadowListChange.foreach {
            val listenerIndex = shadowListeners.indexOf(newShadowListener)
            val innerSumSize = shadowLists.asScala.take(shadowListeners.indexOf(newShadowListener)).foldLeft(0)(_ + _.size)
            backingList.remove(innerSumSize + shadowListChange.from, innerSumSize + shadowListChange.removedTo)
            backingList.addAll(innerSumSize + shadowListChange.from, shadowListChange.addedSubList)
          }
        }
        (newShadowListener, shadowList)
      }
      shadowListeners.insertAll(shadowListsChange.from, newZipped.map(_._1))
      backingList.addAll(sumSize, newZipped.flatMap(_._2.asScala).asJava)
    }
  }
  
  private def unsupportedOperation() = throw new UnsupportedOperationException("read only list")
  
  // All mutations disallowed.
  override def remove(from: Int, to: Int): Unit = unsupportedOperation()
  override def retainAll(elements: E*): Boolean = unsupportedOperation()
  override def removeAll(elements: E*): Boolean = unsupportedOperation()
  override def setAll(collection: java.util.Collection[_ <: E]): Boolean = unsupportedOperation()
  override def setAll(elements: E*): Boolean = unsupportedOperation()
  override def addAll(elements: E*): Boolean = unsupportedOperation()
  override def remove(index: Int): E = unsupportedOperation()
  override def add(index: Int, elment: E): Unit = unsupportedOperation()
  override def set(index: Int, element: E): E = unsupportedOperation()
  override def retainAll(collection: java.util.Collection[_]): Boolean = unsupportedOperation()
  override def removeAll(collection: java.util.Collection[_]): Boolean = unsupportedOperation()
  override def addAll(index: Int, collection: java.util.Collection[_ <: E]): Boolean = unsupportedOperation()
  override def addAll(collection: java.util.Collection[_ <: E]): Boolean = unsupportedOperation()
  override def remove(element: Any): Boolean = unsupportedOperation()
  override def add(element: E): Boolean = unsupportedOperation()
  override def clear(): Unit = unsupportedOperation()
  
  override def removeListener(listener: ListChangeListener[_ >: E]): Unit = backingList.removeListener(listener)
  
  override def addListener(listener: ListChangeListener[_ >: E]): Unit = backingList.addListener(listener)
  
  override def removeListener(listener: InvalidationListener): Unit = backingList.removeListener(listener)
  
  override def addListener(listener: InvalidationListener): Unit = backingList.addListener(listener)
  
  override def subList(from: Int, to: Int): java.util.List[E] = backingList.immutable.subList(from, to)
  
  override def listIterator(index: Int): java.util.ListIterator[E] = backingList.immutable.listIterator(index)
  
  override def listIterator(): java.util.ListIterator[E] = backingList.immutable.listIterator()
  
  override def lastIndexOf(element: Any): Int = backingList.lastIndexOf(element)
  
  override def indexOf(element: Any): Int = backingList.indexOf(element)
  
  override def get(index: Int): E = backingList.get(index)
  
  override def containsAll(collection: java.util.Collection[_]): Boolean = backingList.containsAll(collection)
  
  override def toArray[E](array: Array[E with Object]): Array[E with Object] = backingList.toArray[E with Object](array) 
  
  override def toArray(): Array[java.lang.Object] = backingList.toArray()
  
  override def iterator(): java.util.Iterator[E] = backingList.immutable.iterator()
  
  override def contains(element: Any): Boolean = backingList.contains(element)
  
  override def isEmpty(): Boolean = backingList.isEmpty()
  
  override def size(): Int = backingList.size()
}

object CompositeObservableList {
  def main(args: Array[String]): Unit = {
    try {
      val composite = new CompositeObservableList[Int]
      val list1: ObservableList[Int] = FXCollections.observableArrayList(0, 1, 2)
      val list2: ObservableList[Int] = FXCollections.observableArrayList(3, 4, 5)
      val list3: ObservableList[Int] = FXCollections.observableArrayList(6, 7, 8, 9)
      val list4: ObservableList[Int] = FXCollections.observableArrayList(10, 11, 12, 13, 14)
      
      composite.lists.add(list1)
      composite.lists.add(list2)
      composite.lists.add(list3)
      composite.lists.add(list4)
      println("1 ===> " + composite.asScala)
      composite.lists.remove(list4)
      println("2 ===> " + composite.asScala)
      list4.remove(0)
      println("3 ===> " + composite.asScala)
      list1.remove(2)
      println("4 ===> " + composite.asScala)
      composite.lists.remove(list1)
      println("5 ===> " + composite.asScala)
      composite.lists.add(list2)
      println("6 ===> " + composite.asScala)
      list2.add(99)
      println("7 ===> " + composite.asScala)
      list2.add(0, 100)
      println("7 ===> " + composite.asScala)
    } catch {
      case e => e.printStackTrace(System.out)
    }
  }
}*/